v2.4.0: license activation required (BREAKING CHANGE)#2530
Merged
DavidsonGomes merged 9 commits intodevelopfrom May 6, 2026
Merged
v2.4.0: license activation required (BREAKING CHANGE)#2530DavidsonGomes merged 9 commits intodevelopfrom
DavidsonGomes merged 9 commits intodevelopfrom
Conversation
Phase 1 of the licensing rollout — backend only. Brings the same
activation lifecycle that already exists in evolution-go (pkg/core)
into evolution-api as src/licensing/, plus a public /license/* router
and a gate middleware that 503s API traffic until activation.
Module layout (mirrors pkg/core/*.go):
- src/licensing/model.ts (config keys, type contracts)
- src/licensing/store.ts (Prisma RuntimeConfig CRUD + hardware-based instance ID)
- src/licensing/endpoint.ts (XOR-decoded URL with parts-array dev fallback)
- src/licensing/transport.ts (axios + HMAC-SHA256 signing)
- src/licensing/integrity.ts (paridade-only stubs - Baileys does not consume)
- src/licensing/runtime.ts (RuntimeContext, initializeRuntime, gateMiddleware,
heartbeat, shutdown, completeActivation)
Public routes (no auth) - same contract as evolution-go:
- GET /license/status -> {status, instance_id, api_key (masked)}
- GET /license/register?redirect_uri -> POST /v1/register/init upstream
- GET /license/activate?code= -> POST /v1/register/exchange + activate
Bootstrap order in src/main.ts:
1. setDB(prisma)
2. initializeRuntime({tier: 'evolution-api', version, globalApiKey})
3. /license router (always public)
4. gateMiddleware (503 LICENSE_REQUIRED before business routers)
5. business routers
6. startHeartbeat (30 min, fire-and-forget)
7. SIGTERM/SIGINT -> POST /v1/deactivate (best-effort)
Behaviour notes:
- AUTHENTICATION_API_KEY is reused as bootstrap key (mirrors GLOBAL_API_KEY in Go).
If a license already exists in the DB, the service runs locally even if the
licensing server is unreachable.
- Gate middleware allowlist: /license/*, /manager/**, /assets/**, /store/**,
/health, /server/ok, /favicon.ico, /ws, common static extensions.
- Heartbeat carries optional telemetry_bundle with messages_sent / messages_recv
that callers can feed via trackMessageSent() / trackMessageRecv().
Schema:
- New Prisma model RuntimeConfig (key/value) on both postgresql and mysql schemas.
Run npm run db:migrate:dev per provider before starting the service.
Endpoint URL ofuscation:
- Set LICENSE_ENDPOINT_ENCODED + LICENSE_ENDPOINT_XOR_KEY (hex) in release builds
to avoid the licensing URL appearing as a plain string in the bundle.
- Dev fallback assembles license.evolutionfoundation.com.br from a parts array,
same technique as evolution-go.
Phase 2 (manager-v2 UI for the activation flow) lands in a separate PR
under evolution-foundation/evolution-manager-v2.
Adds the database migration that creates the licensing storage table (postgres + mysql). This was missing from the previous licensing commit. Without this migration, npm run db:deploy is a no-op and the server will fail to find the table at boot.
Polishes the licensing rollout for public release: - Better error UX: HTTP 503 now carries instance_id, docs_url and an actionable message instructing the operator to open the manager UI or set AUTHENTICATION_API_KEY in .env. - Better boot banner: lists the activation paths (manager UI, env var) with the docs URL and the instance_id. - Auto-detect missing migration: if the RuntimeConfig table is absent, the server prints a clear banner asking the operator to run npm run db:deploy and exits 1, instead of throwing a Prisma stack trace from inside the bootstrap. - Version bump 2.3.7 -> 2.4.0. - CHANGELOG entry with BREAKING CHANGE notice and migration guide. - README section 'License Activation' linking to docs.evolutionfoundation.com.br/licensing.
- Bumps the embedded manager UI to the version published on evolution-foundation/evolution-manager-v2 main, which now includes the license-aware login flow that mirrors evolution-go-manager. - Removes the legacy manager/dist/assets/test-interactive.js stand-alone script — its functionality is now a proper React component (TestInteractiveModal) inside the bundle, accessed from the instance card on the dashboard. - Updates the manager-v2 submodule pointer to track main.
The autofix from the pre-push hook reorders imports, normalizes line breaks and reformats the constructor signature. Also moves DOCS_URL to the top of the module so the auto-detect error path can reference it without hitting the temporal dead zone.
Contributor
There was a problem hiding this comment.
Sorry @DavidsonGomes, your pull request is larger than the review limit of 150000 diff characters
Mirrors evolution-go/tools/build-dist/obfuscate.go: the URL of the licensing server is now XOR-encoded into the JS bundle by tsup `define`, so it never appears as a plain literal in dist/main.js. The Dockerfile accepts the pair as build-args (NOT runtime env vars) so an operator cannot point the running service at a different licensing server. - src/licensing/endpoint.ts: read from compile-time `__LICENSE_ENDPOINT_*__` identifiers replaced by tsup; keep parts-array fallback for dev builds. - tsup.config.ts: `define` reads LICENSE_ENDPOINT_ENCODED / _XOR_KEY from build env at the moment npm run build is invoked. - tools/encode-url.js: helper to generate the hex pair for a given URL. Usage: eval "$(node tools/encode-url.js <url>)". - Dockerfile: ARG + ENV plumbing for the build stage only. - CHANGELOG: notes about the build-time obfuscation.
The manager-v2 source repository is now private, so the CI checkout step fails when trying to fetch the submodule (no PAT configured, GITHUB_TOKEN has no cross-repo read scope). Drop the submodule entirely — the runtime artefact already lives under manager/dist/ in this repo, which is what the Express server serves. Source for the manager continues to be maintained at evolution-foundation/evolution-manager-v2 (private).
| export function activateIntegrity(rc: RuntimeContext): void { | ||
| if (!rc) return; | ||
| runtimeSalt = createHash('sha256') | ||
| .update(rc.apiKey + rc.instanceId + 'ev0') |
| export function deriveInstanceToken(instanceID: string, rc: RuntimeContext): string { | ||
| if (!rc || !rc.isActive()) return ''; | ||
| return createHash('sha256') | ||
| .update(instanceID + rc.apiKey) |
|
|
||
| recomputeContextHash(): void { | ||
| this.ctxHash = createHash('sha256') | ||
| .update(this.apiKey + this.instanceId) |
| if (!rc.isActive()) return [false, rc.registerUrl]; | ||
| // Verify hash integrity. | ||
| const expected = createHash('sha256') | ||
| .update(rc.apiKey + rc.instanceId) |
Previous entry only covered the licensing rollout. The release actually includes 50 commits worth of work: - Manager v2 completely redesigned (Tailwind v4 + @evoapi/design-system, dual-provider support, advanced sessions panels, license flow, Test Interactive modal, full i18n). - Carousel message endpoint (POST /message/sendCarousel). - Cross-client fix for buttons and list rendering on WhatsApp Web/Desktop/iOS via the <biz> stanza node and the legacy listMessage payload. - Interactive buttons via deviceSentMessage with corrected CTA limits and PIX payment_info support. - Catalog orderMessage and quoted productMessage support. - New messaging-history.set event with cumulative counts. - markMessageAsPlayed audio receipt endpoint. - SQS custom base_url. - LID -> phone-number mapping with cache. - Multiple bug fixes (mentionsEveryOne, getLastMessage, markMessageAsRead, list-message JSON cloning, Cloud API race conditions, instance logout idempotency, zombie-instance cleanup, network family timeout, etc.).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces the licensing system on the Evolution API, mirroring the implementation already running in evolution-go (
pkg/core). It is a coordinated release with the new license-aware login flow on evolution-foundation/evolution-manager-v2 (already merged tomain, embedded undermanager/dist/).HTTP 503 LICENSE_REQUIRED. The full migration guide is in the CHANGELOG entry shipped with this PR.What's in here
src/licensing/— new module mirroringpkg/core/:model.ts,store.ts,endpoint.ts,transport.ts,integrity.ts,runtime.tsRuntimeContext, gate middleware, signed/unsigned HTTP transport (HMAC-SHA256), hardware-based instance ID, fire-and-forget heartbeat (30 min), graceful shutdown deactivation.src/api/routes/license.router.ts— public routes:GET /license/status→{status, instance_id, api_key (masked)}GET /license/register?redirect_uri=→ POST/v1/register/initupstreamGET /license/activate?code=→ POST/v1/register/exchange+ activateadd_runtime_config(postgres + mysql) — required (npm run db:deploy).src/main.ts— wire-up: setDB → initializeRuntime → /license router → gateMiddleware → business routers → startHeartbeat → SIGTERM/SIGINT shutdown.RuntimeConfigis absent, the boot prints an actionable banner and exits 1, instead of throwing a Prisma stack trace.instance_id,docs_urland a message instructing the operator to open the manager or setAUTHENTICATION_API_KEY.manager/dist/refreshed fromevolution-foundation/evolution-manager-v2:main— includes the license-aware login flow and removes the legacy stand-alonetest-interactive.js.Activation paths (recap for reviewers)
AUTHENTICATION_API_KEYin.env. The bootstrap path validates it with the licensing server, persists it locally, marks the instance active./manager/login. The manager hits/license/status, seesinactive, calls/license/register?redirect_uri=..., redirects to the licensing server. After the form, callback at/manager/license/callback?code=...exchanges the code, persists the api_key, dashboard becomes accessible.503 LICENSE_REQUIREDwithregister_urlin the body — open it in a browser to activate.Migration guide
If you skip
db:deploy, the new auto-detect path prints a clear banner asking you to run it, and exits.Validation
npx tsup— green; all licensing files compile cleanly.tsc --noEmit— same 56 pre-existing errors that develop has (Baileys type-resolution issue, unrelated to this PR).AUTHENTICATION_API_KEY→ silent activation ✅main.Test plan
npm run db:deploy→ first boot shows bannerAUTHENTICATION_API_KEY→ log showsGlobal API key not acceptedwarning, instance stays inactive (does not crash)db:deployafter upgrade → boot shows new auto-detect banner asking to run db:deploy, exits 1/instance/fetchInstanceswhile inactive → 503 withcode: LICENSE_REQUIRED,register_url,instance_id,docs_url, messagekill -TERM→ /v1/deactivate is sent (best-effort, non-blocking)Related
pkg/core/{runtime,store,transport,endpoint,integrity,model}.go